package licode.unisop.platform.application.executor;

import cn.hutool.core.lang.UUID;
import licode.unisop.client.utils.SopException;
import licode.unisop.client.vo.SopUrl;
import licode.unisop.platform.application.exception.PlatErrorCode;
import licode.unisop.platform.application.gateway.SiPlatProviderManager;
import licode.unisop.platform.client.api.SiPlatformAuthService;
import licode.unisop.platform.client.cache.PlatAuthCache;
import licode.unisop.platform.client.info.PlatformAuthInfo;
import licode.unisop.platform.client.info.PlatformHeartInfo;
import licode.unisop.platform.client.vo.PlatAuthVO;
import licode.unisop.platform.client.vo.PlatformUrl;
import licode.unisop.platform.domain.PlatAuthItem;
import licode.unisop.platform.domain.PlatAuthManager;
import licode.unisop.provider.api.SiPlatformProvider;
import licode.unisop.provider.conf.SiAssetType;
import licode.unisop.provider.info.SiLookupAssetIn;
import licode.unisop.provider.info.SiPlatIdFind;
import licode.unisop.provider.info.SiPlatformId;
import licode.unisop.provider.utils.StrUtil;
import licode.unisop.provider.vo.SiAsset;
import licode.unisop.provider.vo.SiAssetInfo;
import licode.unisop.provider.vo.SiPlatform;
import licode.unisop.provider.vo.SiProps;
import licode.unisop.types.dp.PlatformState;
import licode.unisop.types.event.DomainEvent;
import licode.unisop.types.event.DomainEventHandler;
import licode.unisop.types.event.DomainEventSubscribe;
import licode.unisop.types.event.PlatDomainEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author licode
 */
@Slf4j
@Primary
@Service
public class SiPlatformAuthExecutor implements SiPlatformAuthService, DomainEventHandler {
    @Resource
    private PlatAuthCache platAuthCache;

    @Resource
    private PlatAuthManager platAuthManager;

    @Resource
    private SiPlatProviderManager siPlatProviderManager;

    @Resource
    private DomainEventSubscribe domainEventSubscribe;

    @PostConstruct
    public void postConstruct() {
        domainEventSubscribe.subscribe(PlatDomainEvent.class, this);
    }

    @Override
    public <T extends DomainEvent> void handle(T event) {
        if (event instanceof PlatDomainEvent) {
            PlatDomainEvent platEvt = (PlatDomainEvent) event;
            updateCache(SiPlatformId.builder().id(platEvt.getPlatformId()).build());

            log.info("--- receive platform event -> {}:{}", platEvt.getPlatformId(), platEvt.getEventType());
        }
    }

    private void checkPlatformState(SiPlatform platform) {
        if (!PlatformState.ACTIVATED.getState().equals(platform.getState())) {
            if (PlatformState.NO_ACTIVATE.getState().equals(platform.getState())) {
                throw PlatErrorCode.PLATFORM_NO_ACTIVATED.buildThrow();
            } else if (PlatformState.STOPPED.getState().equals(platform.getState())) {
                throw PlatErrorCode.PLATFORM_FORBIDDEN.buildThrow();
            } else {
                throw PlatErrorCode.PLATFORM_WRONG_STATE.buildThrow();
            }
        }
    }

    @Override
    public void updateCache(SiPlatformId platId) {
        SiPlatIdFind idInfo = SiPlatIdFind.builder()
                .platformId(platId.getId())
                .state(PlatformState.ACTIVATED.getState())
                .build();
        PlatAuthItem platAuth = platAuthManager.findPlatAuthItem(idInfo);

        if (null != platAuth) {
            SiPlatformProvider platPlugin = findPlatformPlugin(idInfo);
            SiPlatform platform = platPlugin.findPlatform(idInfo);
            if (null != platform) {
                buildPlatAuthItem(platAuth, platPlugin, platform);
                platAuthManager.updatePlatAuthItem(platAuth);
            } else {
                platAuthManager.removePlatAuthItem(idInfo);
            }
        }
    }

    @Override
    public PlatAuthVO register(PlatformAuthInfo info) {
        PlatAuthItem authItem;
        SiPlatIdFind idInfo = SiPlatIdFind.builder()
                .platformId(info.getId())
                .build();
        SiPlatformProvider platPlugin = findPlatformPlugin(idInfo);
        SiPlatform platform = platPlugin.findPlatform(idInfo);
        if (null == platform) {
            // 平台不存在
            throw PlatErrorCode.PLATFORM_NOT_EXISTED.buildThrow();
        }

        checkPlatformState(platform);

        if (null == info.getSecret() ||
                !info.getSecret().equals(platform.getSecret())) {
            throw PlatErrorCode.WRONG_PLATFORM_KEY.buildThrow();
        }

        authItem = platAuthManager.findPlatAuthItem(idInfo);

        if (null == authItem) {
            authItem = new PlatAuthItem();
            buildPlatAuthItem(authItem, platPlugin, platform);
            platAuthManager.updatePlatAuthItem(authItem);

            log.info("--- platform auth, 平台注册成功，message：{}", authItem.getPlatformAuth());
        }

        return authItem.getPlatformAuth();
    }

    private void handlePlatformUrls(PlatAuthItem authItem) {
        SiAssetInfo assetInfo = authItem.getAssetInfo();
        if (null != assetInfo.getAssets()) {
            Map<String, PlatformUrl> urls = new HashMap<>();
            for (SiAsset asset : assetInfo.getAssets().values()) {
                if (SiAssetType.AT_URI.equals(asset.getType()) && null != asset.getProps()) {
                    String urlStr = asset.getProps().getString("$urls", "");
                    int filterType = asset.getProps().getInt("$filterType", -1);
                    if ((filterType == 0 || filterType == 1) && StrUtil.isNotBlank(urlStr)) {
                        PlatformUrl platUrl = new PlatformUrl();

                        platUrl.setUrl(urlStr);
                        platUrl.setType(filterType);

                        urls.put(urlStr, platUrl);
                    }
                }
            }

            authItem.getPlatformAuth().setUrls(new ArrayList<>(urls.values()));
        }
    }

    private void buildPlatAuthItem(PlatAuthItem authItem,
                                           SiPlatformProvider platPlugin,
                                           SiPlatform platform) {
        SiAssetInfo assetInfo;

        assetInfo = platPlugin.getAuthPlatAsset(SiPlatIdFind.builder()
                .platformId(platform.getId())
                .build());
        authItem.setPlatform(platform);
        authItem.setAssetInfo(assetInfo);

        authItem.setPlatformAuth(buildPlatAuth(platform));

        /*
         * 处理下平台地址
         */
        handlePlatformUrls(authItem);
    }

    @Override
    public PlatAuthVO fromId(SiPlatIdFind idInfo) {
        return platAuthCache.getPlatformById(idInfo);
    }

    @Override
    public SiPlatform getAuthPlatform(SiPlatIdFind idInfo) {
        PlatAuthItem authItem = platAuthManager.findPlatAuthItem(idInfo);
        return authItem == null ? null : authItem.getPlatform();
    }

    @Override
    public SiPlatform getPlatformAndCheck(SiPlatIdFind idInfo) {
        PlatAuthItem authItem = platAuthManager.findPlatAuthItem(idInfo);
        if (null != authItem) {
            checkPlatformState(authItem.getPlatform());
            return authItem.getPlatform();
        } else {
            return null;
        }
    }

    @Override
    public SiAssetInfo getPlatformAssets(SiPlatIdFind idInfo) {
        PlatAuthItem authItem = platAuthManager.findPlatAuthItem(idInfo);
        if (null != authItem) {
            return authItem.getAssetInfo();
        } else {
            return null;
        }
    }

    @Override
    public List<SiAsset> lookPlatformAsset(SiLookupAssetIn lookupIn) {
        PlatAuthItem platAuth = platAuthManager.findPlatAuthItem(SiPlatIdFind.builder()
                .platformId(lookupIn.getPlatformId()).build());
        if (null != platAuth && null != platAuth.getAssetInfo()) {
            SiAssetInfo assetInfo = platAuth.getAssetInfo();
            return assetInfo.getAssets().values().stream()
                    .filter(item -> item.getType().equals(lookupIn.getAssetType()))
                    .collect(Collectors.toList());
        } else {
            return new ArrayList<>();
        }
    }

    @Override
    public long getAuthCount() {
        return platAuthCache.getAuthCount();
    }

    @Override
    public void forbid(SiPlatformId idInfo) {

    }

    @Override
    public String renew(PlatformHeartInfo info) {
        SiPlatIdFind platInfo = SiPlatIdFind.builder()
                .platformId(info.getPlatformId())
                .tenantId(info.getTenantId())
                .build();
        PlatAuthItem authItem = platAuthManager.findPlatAuthItem(platInfo);

        if (null != authItem && !authItem.getPlatform().getSecret().equals(info.getPlatformSecret())) {
            throw new SopException(PlatErrorCode.WRONG_PLATFORM_KEY.code(),
                    PlatErrorCode.WRONG_PLATFORM_KEY.message());
        }

        if (null == authItem ||
                !authItem.getPlatformAuth().getToken().equals(info.getPlatformToken())) {
            throw new SopException(PlatErrorCode.PLATFORM_NOT_AUTH.code(),
                    PlatErrorCode.PLATFORM_NOT_AUTH.message());
        }

        return "ok";
    }

    private PlatAuthVO buildPlatAuth(SiPlatform platform) {
        PlatAuthVO platAuth = new PlatAuthVO();

        platAuth.setName(platform.getName());
        platAuth.setModelToken(UUID.fastUUID().toString().replace("-", ""));
        platAuth.setToken(UUID.fastUUID().toString());
        platAuth.setInnerToken(UUID.fastUUID().toString().replace("-", ""));
        platAuth.setRealmId(platform.getRealmId());
        platAuth.setPlatformId(platform.getId());
        platAuth.setHomeUrl(platform.getHomeUrl());
        platAuth.setTicketUrl(platform.getTicketUrl());
        platAuth.setConfigs(platform.getProps());

        return platAuth;
    }

    private SiPlatformProvider findPlatformPlugin(SiPlatIdFind info) {
        return siPlatProviderManager.findPlatProvider(info);
    }
}
